----PlayWriter: Adventures in Space----
A 4am crack                  2020-12-31
---------------------------------------

Name: PlayWriter: Adventures in Space
Genre: productivity
Year: 1984
Publisher: Woodbury Computer Associates
Platform: Apple ][+ or later
Media: 5.25-inch disk
Sides: 1
OS: DOS 3.3
Previous cracks: none

                   ~

               Chapter 0
 In Which Various Automated Tools Fail
          In Interesting Ways


COPYA
  fails about halfway through

Locksmith Fast Disk Backup
  copies everything except T14,S06, but
  copy loads almost all the way then
  says "PlayWriter Can ONLY Be Run from
  Your Original Master Diskette" and
  hangs

EDD 4 bit copy (no sync, no count)
  works

Copy ][+ nibble editor
  "bad" sector seems to exist and
  appears normal

Disk Fixer
  as expected, reading the "bad"
  sector fails, but going to
  INPUT/OUTPUT CONTROL (press "O") and
  setting CHECKSUM ENABLED = NO, the
  sector is readable and appears empty

Why didn't COPYA work?
  intentionally corrupted sector

Why didn't Locksmith FDB work?
  presumably a runtime check to ensure
  the corrupted sector is unreadable

EDD worked. What does that tell us?
  probably just a runtime check on the
  bad sector and not anything crazy
  like half or quarter tracks

Booting the original disk, I hear the
familiar disk grinding / recalibration
of a failed attempt to read a bad
sector, but it's very late in the boot
process -- after the title screen, after
the copyright text screen, while it says
"loading Playfiler". Tracing from the
earliest stage of the boot process is
overkill, so I will tackle it from
another direction.

                   ~

               Chapter 1
  In Which We Flail in All Directions


Firstly, always search for the error
message, in case the protection code is
nearby. I searched for different parts
of the (rather long) protection failure
message, and eventually I found it in
low-ASCII on track $1D:

                 --v--

-------------- DISK EDIT --------------
TRACK $1D/SECTOR $08/VOLUME $FE/BYTE$F8
---------------------------------------
...
$C8: 54 CF 0D 49 0B 65 00 50   TO.I.e.P
$D0: 6C 61 79 57 72 69 74 65   layWrite
$D8: 72 20 43 61 6E 20 4F 4E   r Can ON
$E0: 4C 59 20 42 65 20 52 75   LY Be Ru
$E8: 6E 20 66 72 6F 6D 20 59   n from Y
$F0: 6F 75 72 20 4F 72 69 67   our Orig
$F8:>69<6E 61 6C 20 4D 61 73   inal Mas

                 --^--

Unfortunately, the surrounding bytes do
not appear to be code. At least, they
are recognizable as neither assembly
language nor BASIC.

Secondly, always check the disk map in
Copy ][+, in case the corrupted sector
is part of a file. (I have seen this on
other disks. It allows you to write the
protection logic entirely in BASIC or
some other high-level language.)

                 --v--

   TRACK           1               2
   0123456789ABCDEF0123456789ABCDEF012

S0 ...GALUWT    MKEH.AAAAAGDGGEEDCBBBB
E1 ...GALUWT    MKEH.AAAAAGDGGEEDCBBBB
C2 ...RAFUW     MKEH.AAAAAGDGGEEDBBBBB
T3 ...RAFUW   Q MKEH.AAAAAGDGGEEDBBBBB
O4 ...RAFUW   QJMJEH.AAAAAGGGGEEDBBBBB
R5 ...RACPW   OJXJEH.AAAAAGGGGEEDBBBBB
 6 ...RACPW   OMXJEH.AA.AAGGGFEEDBBBBB
 7 ...RABPW   MMXIIE.AAAAAGGGFEEDBBBBB
 8 ...RABPW   MMNIID.AAAAAGGGFEEDBBBBB
 9 ...RABPW    MN ID.AAAAAGGGFEEDBBBBB
.A ...RABZW    MK ID.AAAAAGGGFEEDBBBBB
 B ...RAYZV S  MK ID.AAAAAAGGFEECBBBBB
 C ...RAYIV S  MK ID.AAAAAAGGFEDCBBBBB
 D ...RLYIV Q  MK ID.AAAAAAGGFEDCBBBBB
 E ...RLYIH    MK HD.AAAAAAGGFEDCBBBBB
 F ...RLYWH    MKEHD.AAAAAAGGEEDCBBBBB

                 --^--

Tracks 0-2 are DOS; track $11 is the
disk catalog. The only sector that is
marked as used but not mapped to a file
is... T14,S06, the unreadable sector.

Combined with the fact that the original
disk audibly grinds like any DOS 3.3
disk would when it tries and fails to
read a sector, this strongly implies
that I am looking for a standard sector
read followed by code that checks that
the read failed successfully.

How do you read a sector under DOS 3.3?
Since there's no file mapped to that
sector, we're probably looking for an
assembly language routine. (It would be
difficult, though not impossible, to set
up the parameters entirely from BASIC.)

  - JSR $BD00, the most "low-level"
    entry point that doesn't involve
    fiddling with softswitches.
    Searching the disk for "20 00 BD"
    finds only the one expected match on
    T00,S01, which is part of DOS.

  - JSR $B7B5, the "higher level" entry
    point. Searching the disk for "20 B5
    B7" finds only the expected matches
    within the DOS area on tracks 0-2.

I am nowhere.

                   ~

               Chapter 2
In Which We Have A Flash of Insight (*)
       And Finally Get Somewhere

(*) Luck


Still nowhere in finding the protection
routine, I have a crazy idea: put my
original disk in slot 6, but boot my
non-working copy from slot 5.

Hear me out. The disk boots DOS 3.3
(seemingly unmodified) and the program
is file-based, which means it should
boot from any slot unless they're going
out of their way to force slot 6. But
the protection routine might assume the
disk is in slot 6. (Many protection
routines do this.)

[S6,D1 = original disk]
[S5,D1 = non-working copy]

]PR#5
...read read read...
...switches to slot 6 briefly...
...grinds...
...protection check passes...
...continues to main menu...

Aha! But how does this help us? I'm not
sure yet. But the bad sector is track
$14, sector $06. Maybe the protection
routine has its own RWTS parameter
table, including a hard-coded reference
to slot 6?

Turning once again to my trusty Disk
Fixer sector editor, a search for the
byte sequence $14 $06 finds several
matches:

                 --v--

------------- DISK SEARCH --------------

$0D/$03-$05   $0D/$03-$0E   $0D/$08-$16
$13/$0E-$4A   $1C/$0C-$E6

                 --^--

Most of those are unrelated data...
except the match on track $1C, which
looks like this:

                 --v--

-------------- DISK EDIT --------------
TRACK $1C/SECTOR $0C/VOLUME $FE/BYTE$E6
---------------------------------------
$B8: CF 0B 5A B5 19 BB 8F D0   OKZ5Y;.P
$C0: 2B D6 B9 61 CF A9 00 85   +V9!O)@.
$C8: 09 A9 61 A0 FA 20 D9 03   I)! z YC
$D0: AD 07 62 C9 40 D0 05 A9   -G"I@PE)
$D8: 00 85 08 60 A9 01 D0 F9   @.H )APy
$E0: 00 00 01 60 01 FE>14<06   @@A A~TF
$E8: 0B 62 89 52 00 00 01 00   K".R@@A@
$F0: 00 60 01 00 01 EF D8 00   @ A@AoX@
$F8: 00 00 00 00 00 00 00 00   @@@@@@@@

                 --^--

Thta is definitely an RWTS parameter
table, starting at byte offset $E2. As
I thought, it hard-codes the slot and
drive ($60 for slot 6, $01 for drive 1)
as well as the track and sector number
of the intentionally corrupted sector.

But wait, there's more! Immediately
before this parameter table looks like
executable code.

                 --v--

----------- DISASSEMBLY MODE ----------
00C5:A9 00          LDA   #$00
00C7:85 09          STA   $09

; address of RWTS parameter table
00C9:A9 61          LDA   #$61
00CB:A0 FA          LDY   #$FA

; execute RWTS command (read)
00CD:20 D9 03       JSR   $03D9

; check RWTS error code
00D0:AD 07 62       LDA   $6207

; is it "drive error"?
00D3:C9 40          CMP   #$40

; no -> branch to failure path
00D5:D0 05          BNE   $00DC

; read failed successfully (original)
00D7:A9 00          LDA   #$00
00D9:85 08          STA   $08
00DB:60             RTS

; read unexpectedly succeeded (copy)
00DC:A9 01          LDA   #$01
00DE:D0 F9          BNE   $00D9

                 --^--

Several things to note here. Firstly, I
forgot that there was an even higher-
level entry point to the DOS 3.3 RWTS,
the vector at $3D9. This contains a JMP
$B7B5, unless you have a really old
program running on a really old Apple II
with less than 48K, in which case the
RWTS entry point will be at a lower
address like $77B5 or even $37B5.

Silly me for forgetting this.

Secondly, and more importantly, I see
that the protection routine is not
directly tied to the error message. If
it fails, the only difference is the
value that ends up in zero page $08 --
#$00 for an original, #$01 for a copy.
To bypass the protection, we just need
to ensure that $08 ends up with #$00.

; change code at offset $C9 to branch
; to success path at offset $D7
T1C,S0C,$C9: A9 61 -> F0 0E

The final code looks like this:

                 --v--

----------- DISASSEMBLY MODE ----------
00C5:A9 00          LDA   #$00
00C7:85 09          STA   $09

; always branches
00C9:F0 0E          BEQ   $00D9
...
00D9:85 08          STA   $08
00DB:60             RTS

                 --^--

Quod erat liberandum.

---------------------------------------
A 4am crack                    No. 2303
------------------EOF------------------
